#pragma once
#include <glm/glm.hpp>
#include <stdexcept>// std::invalid_argument
#include <sstream>// std::ostringstream

class CPointxz;
using P = CPointxz;

using Vec2 = glm::vec2;
using Vec3 = glm::vec3;
using Vec4 = glm::vec4;
using Mat = glm::mat4;
using Mat3 = glm::mat3;
using Mat2 = glm::mat2;

#define PtrOf(value) glm::value_ptr(value)

#define Mat_Zero Mat(0)
#define Mat_Identity Mat(1)
#define Axis_Y Vec3(0, 1, 0)

/** 
 *@brief CPointxz(x,z) is more easy to use when develping 3D Scene, because almost all opereation is on OpenGL's XZ-Plane.
 *@note  the alignment of class bytes is important when using in gl functions as argument type: `void\*`
 */
class CPointxz {
public:
	enum { _invalid_ = 123321 };
	explicit CPointxz() : x(_invalid_), z(_invalid_) {}
	explicit CPointxz(float a) : x(a), z(a) {}
	explicit CPointxz(float x, float z) : x(x), z(z) {}

	CPointxz operator+(const CPointxz& other) const { return CPointxz(this->x + other.x, this->z + other.z); }
	CPointxz operator*(const float f) const { return CPointxz(this->x * f, this->z * f); }
	CPointxz operator/(const float f) const {
		if (f == 0) throw std::invalid_argument("Division by zero is not allowed.");
		return CPointxz(this->x / f, this->z / f);
	}
	CPointxz& operator+=(const CPointxz& other) {
		this->x += other.x;
		this->z += other.z;
		return *this;
	}

	friend std::ostream& operator<<(std::ostream& os, const CPointxz& p) {
		os << p.toString();
		return os;
	}

	inline float distanceTo(const CPointxz& p) { return sqrt(pow(x - p.x, 2) + pow(z - p.z, 2)); }

	inline bool isValid() { return x != _invalid_ && z != _invalid_; }

	inline Vec2 toVec2() const { return Vec2(x, z); }
	inline Vec3 toVec3(float y) const { return Vec3(x, y, z); }

	std::string toString() const {
		std::ostringstream oss;
		oss << "(" << x << "," << z << ")";
		return oss.str();
	}

	float x, z;
};